﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;
using NLog;


namespace ASoft.Service
{
    /// <summary>
    /// 串口开发辅助类
    /// </summary>
    public class SerialPortService
    {
        /// <summary>
        /// 接收事件是否有效 false表示有效
        /// </summary>
        public bool ReceiveEventFlag = false;


        private SerialPort serialPort = new SerialPort();

        /// <summary>
        /// 串口号
        /// </summary>
        public string PortName { get; set; }

        /// <summary>
        /// 波特率
        /// </summary>
        public int BaudRate { get; set; }

        /// <summary>
        /// 奇偶校验位
        /// </summary>
        public Parity Parity { get; set; }

        /// <summary>
        /// 数据位
        /// </summary>
        public int DataBits { get; set; }

        /// <summary>
        /// 停止位
        /// </summary>
        public StopBits StopBits { get; set; }


        /// <summary>
        /// 参数构造函数（使用枚举参数构造）
        /// </summary>
        /// <param name="portName">串口号</param>
        /// <param name="baudRate">波特率</param>
        /// <param name="parity">奇偶校验位</param>
        /// <param name="dataBits">数据位</param>
        /// <param name="stopBits">停止位</param>
        public SerialPortService(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits)
        {
            PortName = portName;
            BaudRate = baudRate;
            Parity = parity;
            DataBits = dataBits;
            StopBits = stopBits;

            serialPort.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived);
            serialPort.ErrorReceived += new SerialErrorReceivedEventHandler(serialPort_ErrorReceived);
        }

        public SerialPortService()
            : this("COM3", 9600, Parity.None, 8, StopBits.One)
        {
        }


        /// <summary>
        /// 端口是否已经打开
        /// </summary>
        public bool IsOpen { get; private set; }

        /// <summary>
        /// 打开端口
        /// </summary>
        /// <returns></returns>
        public void OpenPort()
        {
            IsOpen = true;
            if (serialPort.IsOpen)
            {
                serialPort.Close();
            }

            OpenPortEx(serialPort);
        }

        void OpenPortEx(SerialPort sPort)
        {
            if (!IsOpen)
            {
                return;
            }
            if (sPort.IsOpen)
            {
                return;
            }
            sPort.PortName = PortName;
            sPort.BaudRate = BaudRate;
            sPort.Parity = Parity;
            sPort.DataBits = DataBits;
            sPort.StopBits = StopBits;

            sPort.Open();
            RC.Current.Loger.Trace(string.Format("SerialPort Open:{0}", Format(sPort)));
        }

        /// <summary>
        /// 关闭端口
        /// </summary>
        public void ClosePort()
        {
            IsOpen = false;
            if (serialPort.IsOpen)
            {
                serialPort.Close();
                RC.Current.Loger.Trace(string.Format("SerialPort Close:{0}", Format(serialPort)));
            }
        }

        /// <summary>
        /// 丢弃来自串行驱动程序的接收和发送缓冲区的数据
        /// </summary>
        public void DiscardBuffer()
        {
            serialPort.DiscardInBuffer();
            serialPort.DiscardOutBuffer();
        }

        /// <summary>
        /// 数据接收处理
        /// </summary>
        void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            //禁止接收事件时直接退出
            if (ReceiveEventFlag)
            {
                return;
            }

            byte[] data = new byte[serialPort.BytesToRead];
            serialPort.Read(data, 0, data.Length);
        }

        /// <summary>
        /// 错误处理函数
        /// </summary>
        void serialPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
        {
            //throw new ErrorReceivedException(e);
            RC.Current.Loger.Trace(e.ToString());
        }

        #region 数据写入操作

        /// <summary>
        /// 写入数据
        /// </summary>
        /// <param name="msg"></param>
        public void WriteData(string msg)
        {
            CheckPortStatus();
            serialPort.Write(msg);
        }

        /// <summary>
        /// 写入数据
        /// </summary>
        /// <param name="msg">写入端口的字节数组</param>
        public void WriteData(byte[] msg)
        {
            CheckPortStatus();
            serialPort.Write(msg, 0, msg.Length);
        }

        public void CheckPortStatus()
        {
            if (!IsOpen)
            {
                throw new Exception("串口设备未打开，无法操作.");
            }
            OpenPortEx(serialPort);
        }

        /// <summary>
        /// 发送串口命令
        /// </summary>
        /// <param name="sendData">发送数据</param>
        /// <param name="receiveData">接收数据</param>
        /// <param name="overtime">超时时间</param>
        /// <returns></returns>
        public bool SendCommand(byte[] sendData, ref  byte[] receiveData, int overtime)
        {
            CheckPortStatus();
            try
            {
                //关闭接收事件
                ReceiveEventFlag = true;
                //清空缓冲区
                DiscardBuffer();
                if (sendData != null && sendData.Length > 0)
                {
                    serialPort.Write(sendData, 0, sendData.Length);
                    RC.Current.Loger.Trace(string.Format("SendCommand Write:{0}", AppUtility.GetByteString(sendData)));
                }

                if (receiveData == null || receiveData.Length == 0)
                {
                    return false;
                }

                System.Threading.Thread.Sleep(50 * overtime);
                if (!serialPort.IsOpen)
                {
                    return false;
                }
                if (serialPort.BytesToRead < receiveData.Length)
                {
                    return false;
                }

                var readValue = new byte[serialPort.BytesToRead];
                var ret = serialPort.Read(readValue, 0, readValue.Length);
                var result = false;

                for (int i = 0; i < readValue.Length - 3; i++)
                {
                    if (readValue[i] == receiveData[0]
                        && readValue[i + 1] == receiveData[1]
                        && (receiveData[2] == 0 || receiveData[2] == readValue[i + 2]))
                    {
                        var startIndex = 1;
                        if (receiveData[2] != 0)
                        {
                            startIndex = 2;
                        }
                        for (int j = startIndex; j < receiveData.Length; j++)
                        {
                            receiveData[j] = readValue[i + j];
                        }
                        result = true;
                        break;
                    }
                }

                RC.Current.Loger.Trace(string.Format("SendCommand ReadRet:{0}", ret));
                if (result)
                {
                    RC.Current.Loger.Trace(string.Format("SendCommand Read:{0}", AppUtility.GetByteString(receiveData)));
                }
                ReceiveEventFlag = false;       //打开事件
                return result;
            }
            finally
            {
                ReceiveEventFlag = false;
            }
        }

        #endregion

        /// <summary>
        /// 封装获取串口号列表
        /// </summary>
        /// <returns></returns>
        public static string[] GetPortNames()
        {
            return SerialPort.GetPortNames();
        }

        ~SerialPortService()
        {
            ClosePort();
        }

        #region 格式转换
        /// <summary>
        /// 转换十六进制字符串到字节数组
        /// </summary>
        /// <param name="msg">待转换字符串</param>
        /// <returns>字节数组</returns>
        public byte[] HexToByte(string msg)
        {
            msg = msg.Replace(" ", "");//移除空格

            //create a byte array the length of the
            //divided by 2 (Hex is 2 characters in length)
            byte[] comBuffer = new byte[msg.Length / 2];
            for (int i = 0; i < msg.Length; i += 2)
            {
                //convert each set of 2 characters to a byte and add to the array
                comBuffer[i / 2] = (byte)Convert.ToByte(msg.Substring(i, 2), 16);
            }

            return comBuffer;
        }

        /// <summary>
        /// 转换字节数组到十六进制字符串
        /// </summary>
        /// <param name="comByte">待转换字节数组</param>
        /// <returns>十六进制字符串</returns>
        public string ByteToHex(byte[] comByte)
        {
            StringBuilder builder = new StringBuilder(comByte.Length * 3);
            foreach (byte data in comByte)
            {
                builder.Append(Convert.ToString(data, 16).PadLeft(2, '0').PadRight(3, ' '));
            }

            return builder.ToString().ToUpper();
        }
        #endregion

        /// <summary>
        /// 检查端口名称是否存在
        /// </summary>
        /// <param name="port_name"></param>
        /// <returns></returns>
        public bool Exists(string port_name)
        {
            foreach (string port in SerialPort.GetPortNames()) if (port == port_name) return true;
            return false;
        }

        public override string ToString()
        {
            return string.Format("{0}，{1}，{2}，{3}，{4}",
                 this.PortName,
                 this.BaudRate,
                 this.Parity,
                 this.DataBits,
                 this.StopBits);
        }

        /// <summary>
        /// 格式化端口相关属性
        /// </summary>
        /// <param name="port"></param>
        /// <returns></returns>
        public static string Format(SerialPort port)
        {
            return String.Format("{0} ({1},{2},{3},{4},{5})",
                port.PortName, port.BaudRate, port.DataBits, port.StopBits, port.Parity, port.Handshake);
        }
    }

    public class ErrorReceivedException : Exception
    {
        public ErrorReceivedException(SerialErrorReceivedEventArgs e)
        {
            this.EventArgs = e;
        }

        public SerialErrorReceivedEventArgs EventArgs { get; private set; }
    }
}
